package annotool.select;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Vector;
import mpicbg.imagefeatures.Feature;
import mpicbg.imagefeatures.FloatArray2DSIFT;
import mpicbg.models.PointMatch;


public class SiftFeatureSelector 
{
	public int featureSize;
	float[][] features;
	int[] trainingTargets;
	int length = 0;
	int dimension = 0;
	float[][][] featuresperimage;
	int[] targetclass;
	//int[][] possitiveImages; 
	int maxClass;
	int iValue;
	int jValue;
	int kValue;
	int mValue;
	int cnts;
	int totalfeats=0;
	float x,y;
	
	

	private int numberofFeatures;
	// closest/next closest neighbour distance ratio
	private float ROD, M_SCORE_THRESHOLD;

	public SiftFeatureSelector(float[][] features, int maxClass, int length, int[] trainingTargets, int featureSize) 
	{
		//discrete before mRMR selection
		this.featureSize = featureSize;
		this.features = features;
		this.maxClass = maxClass;
		this.trainingTargets = trainingTargets;
		this.length = length;
	}
	//return the selected features in vectors

	public float[][] select(int numberofFeatures, float ROD, float M_SCORE_THRESHOLD)
	{
		this.numberofFeatures = numberofFeatures;
		this.ROD = ROD;
		this.M_SCORE_THRESHOLD = M_SCORE_THRESHOLD;
		//ImagePlus[] data1 = problem.readImagePlus(dir,ext);
		featuresperimage = new float[features.length][][];

		float[][] returnfeatures = new float[maxClass][];
		float[][] returnscores = new float[maxClass][];
		
		//to write the scores into a file
		/*File outz = new File("/home/divakaruni/outz-scores.txt");
		FileOutputStream fos = null;
		try {
			fos = new FileOutputStream(outz);
		} catch (FileNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
	      BufferedOutputStream bos = new BufferedOutputStream(fos);
	      OutputStreamWriter out = null;
	      try {
			out = new OutputStreamWriter(bos, "US-ASCII");
		} catch (UnsupportedEncodingException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}*/
	      
		int a;
		targetclass = new int[length];
		//possitiveImages = new int[length][];

		for(int j=0; j<trainingTargets.length; j++)
		{
			targetclass[j] = trainingTargets[j];
			//System.out.print(trainingTargets[i][j]+ "train ");
		}

		//make sure that they are
		//targetmap is for converting back from new targets to original targets
		//key: new target; value: original target
		java.util.HashMap targetmap = new  java.util.HashMap();
		//map the original target to new target, just for converting forward
		java.util.HashMap orig2new = new  java.util.HashMap();
		convertTargets( trainingTargets , targetmap, orig2new);
		int [] numTargets = new   int [ maxClass ];
		for ( int  j=0; j< targetmap.size(); j++)
		{
			numTargets[j] = 0;
			for ( int  i =0 ; i < length ; i++)
			{
				if  ( trainingTargets [i] == ((Integer) targetmap.get(j)).intValue())
					numTargets[j]++;
			}
		}

		//put image index of each group into a 2D array
		int [][] groupedImages = new   int [ maxClass ][];

		for ( int  j=0; j< targetmap.size(); j++)
		{
			groupedImages[j] = new   int [numTargets[j]];
			int  index = 0;      
			for ( int  i =0 ; i < length ; i++)
			{
				if  ( trainingTargets [i] == ((Integer) targetmap.get(j)).intValue())
				{
					groupedImages[j][index] = i;
					index ++;
				}
			}
		}
		//converts 2-D features into 3-D
		for(int i = 0;i < features.length; i++)
		{
			featuresperimage[i] = new float[(features[i].length)/featureSize][featureSize];

			for(int j = 0;j < (features[i].length)/featureSize; j++)
			{

				for(int k = 0;k < featureSize;k++)
				{
					featuresperimage[i][j][k] = features[i][(j * featureSize) + k];
				}

			}
		}

		///
		int f = 0;
		// for every class, n takes first element of each class. 
		// And till total feature size.
		for(int n = 0; n < groupedImages.length; n++)
		{
			//one cycle
			//creating total class features for sorting
		//	Vector<ClassFeatures> classfeatures = new Vector<ClassFeatures>();
			//Vector<ConsensusFeatures> totalConsensusFeatures = new Vector<ConsensusFeatures>();
			ArrayList<Float[]> selectedMatchedFeatures = new ArrayList<Float[]>(); //march 25th
			ArrayList<Float> selectedMatchedScores = new ArrayList<Float>(); //march 25th
			// for each image in the class. the length of positive images
			for( int z = 0; z < groupedImages[n].length; z++)
			{
				iValue = groupedImages[n][z];
				Vector<Feature> featureSet1 = new Vector<Feature>();
				for(int perfeat = 0; perfeat < featuresperimage[iValue].length; perfeat++)
					featureSet1.add(new Feature(featuresperimage[iValue][perfeat]));

				int e = 0; 	// initializing the pointer.
				//Declaration
				Vector< Feature > matchedFeatures = new Vector< Feature >();
				ArrayList<Integer> fPosition1 = new ArrayList<Integer>();
				ArrayList<Integer> fPosition2 = new ArrayList<Integer>();
				ArrayList<Float>   mScore = new ArrayList<Float>();

				//total number of features
				int featureCandidates = 0;

				// other possitive images in the class
				for(int j = 0; j < groupedImages[n].length; j++)
				{

					int k = groupedImages[n][j];
					if(k != iValue)
					{
						//System.out.println("the size of the 1st featureperimage s "+i+" and the is "+k);
						Vector<Feature> featureSet2 = new Vector<Feature>();
						for(int perfeat = 0; perfeat < featuresperimage[k].length; perfeat++)
							featureSet2.add(new Feature(featuresperimage[k][perfeat]));

						Vector< PointMatch > candidates = 
							FloatArray2DSIFT.createMatches( featureSet1, featureSet2, ROD );
						//System.out.println("cycle, the size would be: "+candidates.size());
						for ( PointMatch m : candidates )
						{
							float threshold_M = (1 - (m.getrD() * m.getD()));
							if(threshold_M > M_SCORE_THRESHOLD)
							//if(1 - (m.getrD() * m.getD()) > 0.20)
							{
								//sept 16th
								
								//mScore.add((threshold_M * (threshold_M - M_SCORE_THRESHOLD)));
								mScore.add(threshold_M);
								matchedFeatures.add(m.getfD());
								fPosition1.add(m.getFPosition1());
								fPosition2.add(m.getFPosition2());
								//System.out.println("the sel are: "+(1 - (m.getrD() * m.getD()))+" pos1 "+ (m.getfD().location[0])+" pos2 "+ (m.getfD().location[1]));
							}
						}
						//System.out.println("mscores: "+mScore);
						e++;  // incrementing array pointer

						//increment the number of candidates
						featureCandidates += fPosition1.size();

					} // end of if.

				}		// end of j		

				//creating an object to remove duplicates -old
		//		ConsensusFeatures consensusfeatures = new ConsensusFeatures
		//		(matchedFeatures, mScore, fPosition1, fPosition2, (groupedImages[n].length));
				
				//*march25th
				ArrayList<Integer> positionClone = new ArrayList<Integer>(fPosition1);
				//System.out.println("the size is ::: "+featureCandidates);
				for(int i = 0; i < fPosition1.size(); i++)
				{
					int index = fPosition1.get(i);
					if(positionClone.indexOf(index) != -1)
					{
						float totalScore = 0;
						while(positionClone.indexOf(index) != -1)
						{
							totalScore += mScore.get(positionClone.indexOf(index));
							positionClone.remove(positionClone.indexOf(index));
						}
						//cf.add(new ClassFeatures(matchedFeatures.get(i), (totalScore / (groupedImages[n].length - 1)),
								//fPosition1.get(i),fPosition2.get(i)));
						selectedMatchedFeatures.add(matchedFeatures.get(i).toFloats());
						selectedMatchedScores.add((totalScore / (groupedImages[n].length - 1)));
					}
				}
				
				//*end-march25th
				//remove duplicates within the image. -old
		//		Vector<ClassFeatures>classCFeatures = consensusfeatures.removeImageDuplicates();

		//		classfeatures.addAll(classCFeatures);



			} //end of z for
			
			//march 25th
			float[][] selectedFeatures = new float[selectedMatchedFeatures.size()][featureSize];
			float[] selectedScores = new float[selectedMatchedScores.size()];
			for(int i = 0; i < selectedFeatures.length; i++)
			{
				selectedScores[i] = selectedMatchedScores.get(i);
				for(int j = 0; j < selectedFeatures[i].length; j++)
				{
					selectedFeatures[i][j] = selectedMatchedFeatures.get(i)[j];
				}
			}
			QuickSort.sort(selectedScores, selectedFeatures, 0, selectedScores.length - 1);
			for(int i = 0; i < selectedFeatures.length; i++)
			{
				//System.out.println("the selected matched feat: "+selectedFeatures[i][0]+","+selectedFeatures[i][1]+","+selectedFeatures[i][2]+","+selectedFeatures[i][3]);
				//System.out.println("the selected matched scores: "+selectedScores[i]);	//fortesting
			}
			//end march 25th
//--old
//			System.out.println("one pair, the size would be: "+classfeatures.size());
			// sorts the features with respect to M scores
//--old
//			Collections.sort(classfeatures);
//for(int ak = 0; ak < classfeatures.size(); ak++)
//{
	//System.out.println("classfeats: "+classfeatures.get(ak).feature.scale+", "+classfeatures.get(ak).feature.orientation+", "+classfeatures.get(ak).feature.location[0]+", "+classfeatures.get(ak).feature.location[1]);
	//System.out.println("classscores: "+classfeatures.get(ak).mScore);
//}
			//ArrayList<Float> featfloat = new ArrayList<Float>();
//			Float[] featfloats = new Float[classfeatures.size()];
//			classfeatures.toArray(featfloats);
			//System.out.println("the size of flatten array is: "+featfloats.length);
			//for(int index = 0; index < classfeatures.size(); index++)
			//{
			//	for(int size = 0; size < featureSize; size++)
			//	{
			//		featfloat.add(classfeatures.get(index).feature.toFloats()[size]);
			//	}
			//}
			returnfeatures[f] =  new float[numberofFeatures * featureSize];
	//		returnscores[f] =  new float[numberofFeatures * featureSize];
			//for(int num = 0;num < numberofFeatures * featureSize ;num++)
			//{

			//	returnfeatures[f][num] = (float)featfloat.get(num);

			//}
//			returnfeatures[f] = featfloats;
			//reverse order copy
			int returnIndex = 0;
			for(int index = selectedFeatures.length - 1; index > (selectedFeatures.length - 1) - numberofFeatures; index--)
			{
		//		returnscores[f][index] = selectedScores[index];
		//		System.out.print(selectedScores[index]);
		//	    try {
					
					
		//			out.write(Float.toString(selectedScores[index])+" ");
					 
				     
				      
		//		} catch (IOException e) {
					// TODO Auto-generated catch block
		//			e.printStackTrace();
		//		}
			    	 
			    	 
			      
				for(int size = 0; size < featureSize; size++)
				{
					returnfeatures[f][returnIndex] = selectedFeatures[index][size];
					returnIndex++;
				}
			}
	//		try {
	//			out.write("\n\n\nnext\n\n\n");
	//		} catch (IOException e1) {
				// TODO Auto-generated catch block
	//			e1.printStackTrace();
	//		}
	//		System.out.println(" ");
			
			//System.arraycopy(selectedFeatures, selectedFeatures.length - numberofFeatures - 1, returnfeatures[f], 0, numberofFeatures); //march 25th
//			System.out.print("The size would be  "+returnfeatures[f].length);
			//n =  n + (groupedImages[n].length);
			f++;
		}  //end of i for

		///
		//System.out.println("\n\n\nend of new code\n\n\n");
		/*try {
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
		return returnfeatures;
		

	}


	private   int [] convertTargets( int [] targets, HashMap targetmap, HashMap orig2new)
	{
		int [] convertedTargets = new   int [targets. length ];

		int  targetIndex = -1;
		for  ( int  i=0; i < targets. length ; i++)
		{
			if (!targetmap.containsValue(targets[i]))
			{
				targetIndex ++;
				targetmap.put( new Integer(targetIndex), new Integer(targets[i]));
				orig2new.put( new Integer(targets[i]), new Integer(targetIndex));
				convertedTargets[i] = targetIndex;
			}
			else
				convertedTargets[i] = ((Integer) orig2new.get(targets[i])).intValue();
		}     

		int  ngroups = orig2new.size();
		return  convertedTargets;
	} 


}